require 'rubygems' # Unless you install from the tarball or zip.
require 'icalendar'
require 'date'
require 'net/https'
require 'net/http'
require 'rexml/document'
require 'open-uri'
require 'uri'
require 'cgi'
require 'gdata'
#require 'googlecalendar'

include Icalendar # Probably do this in your class to limit namespace overlap

class EndarUserTimeController < ApplicationController 
	EXTENSIONS = {"public/userics" => ["ics"]}
	layout 'standard'
	# list returns the information needed for free/list.rhtml
	def list
		@user_times = EndarUserTime.find(:all)
	end
	# show is called for each @free in free/list.rhtml and user/new.rhtml
	def show
		@user_time = EndarUserTime.find(params[:id])
	end
	
	# new returns the information needed for free/new.rhtml
	def new
		@user_time = EndarUserTime.new
		@event = EndarEvent.find(params[:id])
		@user =  EndarUser.find(params[:uid])
	end
	# create is called when someone clicks the Create button on free/new.rhtml
	def create
		@event = EndarEvent.find(params[:id])
		@user = EndarUser.find(params[:uid])
		@event_times = EndarEventTime.find(:all, :conditions => ["event_id = ?", @event.id])
		#@user_time = EndarUserTime.new(params[:user_times])
		#@user_time[:user_id] = @user.id
		
		if EndarUserTime.find(:first, :conditions => ["user_id = ?", @user.id]) == nil #means there are no user_times for @user.id
			@event_times.each do |e|
				@user.endar_user_times.build(:date_begin => e.date_begin, :date_end => e.date_end, :user_id => @user.id, :is_free => 0)
			end
			if @user.save
				puts "CREATED USER TIMES!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
				#try emailing here..................this doesnt work :(
				#adminEmail = @event[:admin]
				#puts "ADMIN EMAIL: " + adminEmail
				#@admin = EndarUser.find(:all, :conditions => ["name = ?", adminEmail])
				#InviteUsers.deliver_link(adminEmail, @admin, @event)
			end
		end
		
		puts "USER_TIME!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
		puts @user_time
		@user_form_time = EndarUserTime.new(params[:user_times])
		while @user_form_time.date_end - @user_form_time.date_begin >= (60 * 30)
			puts @user_form_time.date_begin
			@user_time = EndarUserTime.find(:first, :conditions => ["user_id = ? AND date_begin = ?", @user.id, @user_form_time.date_begin])
			if not @user_time.nil?
				@user_time.update_attributes("is_free" => 1)
			end
			@user_form_time[:date_begin] = @user_form_time.date_begin.advance(:minutes => 30)
		end
		
		#@user_time = EndarUserTime.find(:first, :conditions => ["user_id = ? AND date_begin = ?", @user.id, EndarUserTime.new(params[:user_times]).date_begin])
		#@user_time = EndarUserTime.find(:first, :conditions => ["user_id = ?", @user.id])
		#if @user_time != nil
		#	@user_time.update_attributes("is_free" => 1)
		redirect_to :controller => 'endar_user', :action => 'show', :id => @event.id, :uid=>@user.id
		#else
		#	redirect_to :controller => 'endar_user', :action => 'error'
		#end
		
		#if @user_time.save
		#	redirect_to :controller => 'endar_user', :action => 'show', :id => @event.id, :uid => @user.id
		#else
		#	redirect_to :controller => 'endar_user', :action => 'error'
		#end
		
		
	end
	
	def duplicate_exist?(datebegin, dateend, uid)
		result = EndarUserTime.find(:first, :conditions => ["id = ? AND date_begin = ? AND date_end = ?", 
									uid, datebegin, dateend])
		 puts "----------------date begin is"
		 puts datebegin
		 puts "----------------date end is"
		puts dateend
		if result.nil?
			puts "nothing found"
			return false
		else
			return true
		end
	end
	
	def save_times
		@event = EndarEvent.find(params[:id])
		puts "parameter USER TIME IS   ~~~~~~~~~~"
		puts params[:user_time]
		if duplicate_exist?(params[:user_time => "date_begin"], params[:user_time => "date_end"], params[:uid])
			redirect_to :controller => 'endar_user', :action => 'show', :id => @event.id, :uid => @user.id
		else
			@user_times = EndarUserTime.new(params[:user_time])
			@user_times[:user_id] = params[:uid]
			if @user_times.save
				redirect_to :controller => 'endar_user_time', :action => 'sort_times', :id => @event.id, :uid =>params[:uid]
			else
				puts @user_times
				redirect_to :controller => 'endar_user', :action => 'error'
			end	
		end
	end
	
	def sort_times
		@event = EndarEvent.find(params[:id])
		@user = EndarUser.find(params[:uid])
		puts "-----------user id is==========="
		puts @user.id
		@user_times = EndarUserTime.find(:all, :conditions =>["user_id = ?", @user.id])
		@event_times = EndarEventTime.find(:all, :conditions => ["event_id = ?", @event.id])
		puts "-----------event id is==========="
		puts @event.id

		@event_times.each do |e|
			@user_times.each do |u|
				puts "one user time here: date begin/date end"
				puts e.date_begin.to_s
				puts e.date_end.to_s
				if e.date_begin >= u.date_begin and e.date_end <= u.date_end
					e.update_attributes("count" => e.count + 1)
					break
				end
			end
		end
		redirect_to :controller => 'endar_user', :action => 'show', :id => @event.id, :uid => @user.id
	end
	
	def parseICS
		@event = EndarEvent.find(params[:id])
		@user = EndarUser.find(params[:uid])
	end

	def createICS
		@user= EndarUser.find(params[:userid])
		@event = EndarEvent.find(params[:id])

		@event_times = EndarEventTime.find(:all, :conditions => ["event_id = ?", @event.id])
		if EndarUserTime.find(:first, :conditions => ["user_id = ?", @user.id]) == nil #means there are no user_times for @user.id
			@event_times.each do |e|
				@user.endar_user_times.build(:date_begin => e.date_begin, :date_end => e.date_end, :user_id => @user.id, :is_free => 1)
			end
			if @user.save
				puts "created user times"
				adminEmail = @event[:admin]
				@users = EndarUser.find(:all, :conditions => ["event_id = ?", @event.id])
				@not_responded = Array.new  #array of users who have not responded
				@responded = Array.new
				@users.each do |u|
					@user_time = EndarUserTime.find(:first, :conditions => ["user_id = ?", u.id])
					if @user_time == nil
						@not_responded.insert(-1, u)
					else
						@responded.insert(-1, u)
					end
				end
				if @user.name != adminEmail
					InviteUsers.deliver_admin(adminEmail, @user, @responded, @not_responded)
				end

			end
		end
		
		#want to email admin
		
		#This save the ics file into server's public/userics/ folder, and the file name and path is saved in variable "post"
		 post = EndarUserTime.save(params[:upload])
		
		parseUserFreeTime(post, @user.id, params[:id])
		redirect_to :controller => 'endar_user', :action => 'show', :id => @event.id, :uid => @user.id
	end
	
	def parseUserFreeTime(parameter, uid, id)
		tz_offset = Rational(-7, 24)

		@user= EndarUser.find(uid)
		#@user= EndarUser.find(params[:userid])
		@event = EndarEvent.find(id)
		@timespanStart = DateTime.parse(@event.date_begin.to_s)
		@timespanEnd = DateTime.parse(@event.date_end.to_s)
		@entries = EndarUserTime.find(:all, :conditions => ["user_id = ?", uid])
		@cal_file = File.open(parameter)
		@cals = Icalendar.parse(@cal_file)
		@accumulatedSchedules = Calendar.new
		@cal = @cals.first
	
		@cals.length.times do |cal|
			@tempCal = @cals[cal]
			@tempCal.events.length.times do |k|
				@tempEvent = @tempCal.events[k]
				#filtering out events without the time specified, ie. "2007-07-04"
				if @tempEvent.dtstart.to_s().length > 10
					@accumulatedSchedules.add_event(@tempEvent)
				end
			end
		end
		@accumulatedSchedules.events = @accumulatedSchedules.events.sort {|x,y| x.dtstart <=> y.dtstart}
		
		puts "===========Before entering loop========="
		@accumulatedSchedules.events.length.times do |i|
			@e = @accumulatedSchedules.events[i]
			puts "printing out dtstart before conversion.............."
			puts @e.dtstart.to_s
			puts @e.dtend.to_s
			puts "timespanStart: " + @timespanStart.to_s
			puts "timespanEnd: " + @timespanEnd.to_s
			puts "WHAT IS THE TIMEZONE??????????"
			puts @cal.timezone.tzid
			#puts "=========converting from UTC to local==========="
			#local = DateTime.now
			@convertedStart = @e.dtstart.new_offset(0)
			puts "@convertedStart: " + @convertedStart.to_s
			
			@convertedEnd = @e.dtend.new_offset(0)
			puts "@convertedEnd: " + @convertedEnd.to_s
			
			#puts "=====end of converting from UTC to local==========="
			#rules = event.recurrence_rules.map{ |rule| rule.orig_value }
			
			#break if busy time is after timespan
			if (@convertedStart >= @timespanEnd) 
				puts "after timespan with event from " + @convertedStart.to_s + " to " + @convertedEnd.to_s
				puts "breaking out of loop...."
				break
			end
			#if busy time is before timespan
			if (@convertedEnd <= @timespanStart)
				puts "before timespan with event from " + @convertedStart.to_s + " to " + @convertedEnd.to_s
				next
			end
			#else
			puts "current event is within timespan..."
			puts "current event is: " + @convertedStart.to_s + " to " + @convertedEnd.to_s
			puts "@entries.length" + @entries.length.to_s
			@entries.length.times do |i|
				entry = @entries[i]
				puts "entering else loop..."
				if (entry[:date_begin] >= @convertedStart && entry[:date_end] <= @convertedEnd)
					puts "entry is within timespan: " + @convertedStart.to_s + " to " + @convertedEnd.to_s
					entry.is_free = 0
					puts "entry.is_free is set to 1"
					entry.save
				end
			end
		end
	end
	
	def readEvents(parameter)
		puts "readEvents is being called................."
		@cal_events = []
		f = File.open(parameter)
		lines = f.readlines
		puts "File has been read in........"
		#puts lines
		inEvent = false
		eventLines = []
		lines.each do |line|
			puts "readEvents - parsing in each line.........."
			puts line
			if line =~ /^BEGIN:VEVENT/ 
				puts "readEvents - encountered BEGIN:EVENT"
				inEvent = true
				eventLines = []
			end
			if inEvent
				eventLines << line
				if line =~ /^END:VEVENT/
					inEvent = false
					puts "readEvents - encountered END:EVENT........"
					@cal_events << parseEvent(eventLines)		
				end
			end
		end
		@cal_events
	end
	
	def parseEvent(lines)
		puts "parseEvent function is being called............"
		event = ICalEvent.new()
		puts "ICalEvent object has been created - event......"
		endDate = nil
		startDate = nil
		rule = nil
		lastLine = 0
		puts "the lines of current event are.............."
		puts lines
		puts "end of event that is passed in.................."
		lines.each do |line|
			puts "current line is "
			puts line
    		if line =~ /^SUMMARY/ 
				puts "this is a summary line"
    		    event.summary = $1
				puts event.summary.to_s
    		elsif line =~ /^DTSTART/ 
				puts "parseEvent - DTSTART is parsed -------"
				temp = line.to_s[8,16]
				puts temp
	   		    startDate = parseDate(temp)
				puts startDate
    		elsif line =~ /^DTEND/ 
				puts "parseEvent - DTEND is parsed -----------"
			    temp = line.to_s[6,14]
				puts temp
				endDate = parseDate(temp)
				puts endDate
    		elsif line =~ /^RRULE/ 
			    puts "parseEvent - RRULE is parsed ---------"
			    rule = line.to_s
				puts rule
            elsif line=~ /^DESCRIPTION/ 
                event.description = $1
				puts "parseEvent - DESCRIPTION is parsed --------"
				puts event.description
			end
		end
		
		event.endDate = endDate
		puts "event.endDate................" + event.endDate.to_s
		event.startDate = startDate
		puts "event.startDate................" + event.startDate.to_s
		#event.parseRecurrenceRule(rule)
		event.rrule = rule
		puts "event.rrule................" + event.rrule.to_s
		event
	end
	
	def parseUserFreeTimeProgress(parameter, uid, id)
		tz_offset = Rational(7, 24)
		@user= EndarUser.find(uid)
		@event = EndarEvent.find(id)
		@timespanStart = DateTime.parse(@event.date_begin.to_s)
		@timespanEnd = DateTime.parse(@event.date_end.to_s)
		@entries = EndarUserTime.find(:all, :conditions => ["user_id = ?", uid])
		@cal_file = File.open(parameter)
		@cals = Icalendar.parse(@cal_file)
		@accumulatedSchedules = Calendar.new
		@cal = @cals.first
		
		@man_parsed_events = readEvents(parameter)
		puts "@man_parsed_events!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
		puts @man_parsed_events
		@man_parsed_events.each do |parsed_event|
			if parsed_event.nil?
				puts "this event is not recurrent.................."
				next
			end
			parsed_event.parseRecurrenceRule
			puts "parsed_event.parseRecurrenceRule has been called"
			#puts parsed_event.frequency
			#puts parsed_event.untilDate
			#puts parsed_event.startDate
			#puts parsed_event.endDate
			
			if (parsed_event.untilDate.nil? || @timespanEnd <= parsed_event.untilDate)
				temp_spanEnd = @timespanEnd
			else
				temp_spanEnd = parsed_event.untilDate
			end
			
			if parsed_event.frequency.to_s.eql? "DAILY"
				puts "this is a daily event"
				current_date = parsed_event.startDate
				
				span = current_date..temp_spanEnd
				span.each do |x|
					#puts "printing out new_dateEnd component......."
					##puts x.year
					#puts x.month
					#puts x.day
					#puts parsed_event.endDate.hour
					#puts x.min
					new_dateEnd = DateTime.civil(x.year, x.month, x.day, parsed_event.endDate.hour, x.min, 0)
					new2_dateEnd = new_dateEnd.new_offset(tz_offset)
					x2 = x.new_offset(tz_offset)
					puts "been converted................"
					puts x2.to_s
					puts new2_dateEnd.to_s
					puts "new_dateEnd created is: " + new2_dateEnd.to_s
					@entries.length.times do |i|
						entry = @entries[i]
						puts "printing out entry's content...."
						puts entry[:date_begin]
						puts entry[:date_end]
						if (entry[:date_begin] >= x2 && entry[:date_end] <= new2_dateEnd)
							entry.is_free  = 0
							entry.save
						end
					end
				end
			end
		end
		
		@cals.length.times do |cal|
			@tempCal = @cals[cal]
			@tempCal.events.length.times do |k|
				@tempEvent = @tempCal.events[k]
				#filtering out events without the time specified, ie. "2007-07-04"
				if @tempEvent.dtstart.to_s().length > 10
					@accumulatedSchedules.add_event(@tempEvent)
				end
			end
		end
		@accumulatedSchedules.events = @accumulatedSchedules.events.sort {|x,y| x.dtstart <=> y.dtstart}
	end
	
	def parseDate(dateStr)
		# We constrain the year to 1970 because Time won't handle lesser years
		# If it is less than 1970 then its probably a birthday or something
		# in which case, we don't really care about the year
		year = dateStr[0,4].to_i
		year = 1970 if year < 1970

		month = dateStr[4,2].to_i
		day = dateStr[6,2].to_i
		hour = dateStr[9,2].to_i
		minute = dateStr[11,2].to_i
		#Time.local(year, month, day, hour, minute)
		DateTime.new(year, month, day, hour, minute, 0)
	end
	
  
	def uploadFileExtensionError
	    render :text => "Sorry, it needs to be iCalendar (.ics) file."
	end

	def parseURL
		@event = EndarEvent.find(params[:id])
		
	end
	
	def signon_google
		#@user = EndarUser.find(params[:uid])
		scope = 'http://www.google.com/calendar/feeds/'
		
		next_url = url_for(:action => 'get_authsubtoken', :id => params[:id],:uid => params[:uid])
		#next_url = 'http://localhost:3000/endar_user_time/log_request'
		
		secure = false  # set secure = true for signed AuthSub requests
		sess = true
		authsub_link = GData::Auth::AuthSub.get_url(next_url, scope, secure, sess)
		
		redirect_to authsub_link
	end
	
	def get_authsubtoken
		@user = EndarUser.find(params[:uid])
		authtoken = params[:token]
		cal = GData::Client::Calendar.new
		cal.authsub_token = params[:token]
		 cal.auth_handler.upgrade()
		
		 @thefeed = cal.get('http://www.google.com/calendar/feeds/default/allcalendars/full')
		@list_of_calendars = @thefeed.to_xml
 		
		theuri = parseGoogleCalendar
		 puts "-------------uri after returning --------------"
		 puts theuri.to_s
		gooduri = URI.parse(theuri.to_s)
		 p gooduri
		 @anotherfeed = cal.get(gooduri.to_s)
		@list_of_events =@anotherfeed.to_xml

		#call to make the xml into ics
		parseGoogleCal(params[:id],params[:uid] )
		
		redirect_to :controller => 'endar_user', :action => 'show', :id => params[:id], :uid => params[:uid]
	end
	
	#parse list of calendar in Google Calendar XML file and return the first calendar in the list.
	def parseGoogleCalendar
		#uid = params[:uid]		
		theuri = ""
		flag = 0
		@list_of_calendars.elements.each("entry") { |entry|
			entry.elements.each { |elem|
				if elem.name == "link"
					if elem.attributes["href"].to_s.include? "private/full"
						theuri = URI.parse( elem.attributes["href"])
						flag = 1
						break
					end
				end
			}
			if flag == 1
				break
			end
		}
		
		return theuri
	end
	
	
	def initializeUserTimeBlock(id, uid, isfree)
		@event_times = EndarEventTime.find(:all, :conditions => ["event_id = ?", id])            
		@created_user_time = EndarUserTime.find(:first, :conditions => ["user_id = ?", uid])
		if @created_user_time == nil #means there are no user_times for @user.id
			@event_times.each do |e|
				@user.endar_user_times.build(:date_begin => e.date_begin, :date_end => e.date_end, :user_id => @user.id, :is_free => isfree)
			end
			if @user.save
				puts "created user times"
			end
		end
	end
	

	# parse Google Calendar XML file that has all events in one calendar of the user
	def parseGoogleCal(id, uid)
		#create a new ics file object
		cal = Calendar.new
					
		#find the timezone of this calendar
		#one loop to get the feed of one calendar.
		@list_of_events.elements.each('entry'){ |entry|
			theevent = Event.new
			theevent.summary = entry.elements['title'].text
			
			if entry.elements['gd:when'].nil?
				#do nothing for now
			else 
				startTime = entry.elements['gd:when'].attributes["startTime"]
				endTime = entry.elements['gd:when'].attributes["endTime"]
				theevent.start =DateTime.parse(startTime).new_offset(of=0)
				theevent.end =DateTime.parse(endTime).new_offset(of=0)				
				cal.add_event(theevent)
			end
		}

		#save ics file object as a file 
		filename = "yourgooglecal.ics"
		# create the file path
		directory = "public/userics"
		path2 = File.join(directory, filename)
		#write the file
		File.open(path2, "wb") { |f| f.write(cal.to_ical) }
		
		initializeUserTimeBlock(id, uid, 1)
		#call to add/subtract user free time!
		parseUserFreeTime(path2, uid, id)
	end
 
	#Codes partially from DZone Snippets http://snippets.dzone.com/posts/show/3994#related
	# Pass things like size, content_type, path on to the downloaded file
    def method_missing(symbol, *args)
      if self.attachment_data.respond_to? symbol
        self.attachment_data.send symbol, *args
      else
        super
      end
    end
	
  
	def parseICSLink
		@user = EndarUser.find(params[:uid])
		@event = EndarEvent.find(params[:id])

		@event_times = EndarEventTime.find(:all, :conditions => ["event_id = ?", @event.id])
		if EndarUserTime.find(:first, :conditions => ["user_id = ?", @user.id]) == nil #means there are no user_times for @user.id
			@event_times.each do |e|
				@user.endar_user_times.build(:date_begin => e.date_begin, :date_end => e.date_end, :user_id => @user.id, :is_free => 1)
			end
			if @user.save
				puts "created user times"
			end
		end
		
		puts "this is the event u want to parse the link"
		puts @event
		#read in the Google calendar ics link and pass the link to parseUserFreeTime
		parameter = params[:link]
		to_pass = parameter.to_s
		start_index = to_pass.index('http:')
		end_index = to_pass.index('.ics') 
		end_index = end_index +3
		url = to_pass[start_index..end_index]
		attachment_data = open(url)
				
        # Grab the path - even though it could be a script and not an actual file
        path = attachment_data.base_uri.path
        # Get the filename from the path, make it lowercase to handle those
        # crazy Win32 servers with all-caps extensions
        filename = File.basename(path).downcase

		# create the file path
		directory = "public/userics"
		path2 = File.join(directory, filename)
		#write the file
		File.open(path2, "wb") { |f| f.write(attachment_data.read) }
		#call to add/subtract user free time!
		parseUserFreeTime(path2, @user.id, params[:id])
		redirect_to :controller => 'endar_user', :action => 'show', :id =>params[:id], :uid => params[:uid]
		
	end
end

class ICalEvent

	def initialize
		@startDate = startDate
		@rrule = nil
		@frequency = nil
		@count = nil
		@untilDate = nil
		@byMonth = nil
		@byDay = nil
	end
	
	def <=>(otherEvent)
		return @startDate <=> otherEvent.startDate
	end
	
	def startsToday?
		startsOn?(Time.now)
	end
	
	def includes?(date)
		return true if date == @startDate
		return false if @untilDate and date > @untilDate
		
		case @frequency
			when 'DAILY'
				#if @untilDate then
				#	return (@startDate..@untilDate).include?(date)
				#end
				increment = @interval ? @interval : 1
				d = @startDate
				counter = 0
				until d > date
					
					if @count then
						counter += 1
						if counter >= @count
							return false
						end
					end

					d += (increment * SECONDS_PER_DAY)
					if 	d.day == date.day and 
						d.year == date.year and 
						d.month == date.month then
						return true
					end
				end
			when 'WEEKLY'
				return true if @startDate.wday == date.wday
			when 'MONTHLY'
			when 'YEARLY'
		end
		false
	end
	
	def to_s
		"IcalEvent is from " + @startDate.to_s + " to " + @endDate.to_s
     #   "<td class='summary'>#{@summary}</td><td class='dates'>#{@startDate.strftime("%Y-%m-%d ")} to #{@endDate.strftime("%Y-%m-%d ")}</td><td class='notes'>#{@description}</td></tr>".delete("\\")
	end
	
	def parseRecurrenceRule
		puts "current rrule is: " + rrule.to_s
		if @rrule =~ /FREQ=(.*?);/ then
			@frequency = $1
			puts "@frequency is " + @frequency.to_s
		end
		
		if @rrule =~ /COUNT=(\d*)/ then
			@count = $1.to_i
			puts "@count is " + @count.to_s

		end
		
		if @rrule =~ /UNTIL=(.*?);/ then
			#@untilDate = DateParser.parse($1)
			@untilDate = parseDatetime($1)				
			puts "@untilDate is " + @untilDate.to_s
		end
		
		if @rrule =~ /INTERVAL=(\d*)/ then
			@interval = $1.to_i
		end

		if @rrule =~ /BYMONTH=(.*?);/ then
			@byMonth = $1
		end

		if @rrule =~ /BYDAY=(.*?);/ then
			@byDay = $1
			#puts "byDay = #{@byDay}"
		end
	end
	
		def parseDatetime(dateStr)
		# We constrain the year to 1970 because Time won't handle lesser years
		# If it is less than 1970 then its probably a birthday or something
		# in which case, we don't really care about the year
		year = dateStr[0,4].to_i
		year = 1970 if year < 1970

		month = dateStr[4,2].to_i
		day = dateStr[6,2].to_i
		hour = dateStr[9,2].to_i
		minute = dateStr[11,2].to_i
		#Time.local(year, month, day, hour, minute)
		DateTime.new(year, month, day, hour, minute, 0)
	end
	
	attr_reader :frequency
	attr_accessor :startDate, :endDate, :summary, :description, :rrule, :frequency, :untilDate
end

